package com.sita.tboard.ui.view;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

import com.sita.bike.R;
import com.sita.bike.utils.FormatUtils;
import com.sita.bike.utils.LogUtils;


public class SpeedMeterView extends View {

    public enum SPEED_STATUS {
        SPEED_STATUS_READY,
        SPEED_STATUS_RUNNING,
        SPEED_STATUS_PAUSED,
        SPEED_STATUS_OVER,
        SPEED_STATUS_MAP
    }

    private final String TAG = SpeedMeterView.class.getSimpleName();
    private SPEED_STATUS mStatus = SPEED_STATUS.SPEED_STATUS_READY;

    // all Bitmap here
    private Bitmap backBitmap;
    private Bitmap runBitmap;
    private Bitmap pointerBitmap;
    private Bitmap timeBitmap;
    private Bitmap rpmBitmap;
    private Bitmap startBitmap;

    // all paint here
    private Paint mBackPaint;
    private Paint mRunPaint;
    private Paint mSpeedTextPaint;
    private Paint mTimeTextPaint;
    private Paint mRPMTextPaint;
    private Paint mRPMUnitTextPaint;
    private Paint mKMHTextPaint;
    private Paint mBackColorPaint;
    private Paint mTestColorPaint;
    private RectF mOval;

    // color here
    private int mBackGroundColor = Color.WHITE;
    private int mValueColor = Color.BLACK;
    private int mUnitColor = Color.GRAY;

    // all value here
    private final int SPEED_MAX = 180;
    private float mAngle = 0f;
    private float mPreAngle = 0f;
    private final float mMaxValue = SPEED_MAX;
    private float mCurValue = 0f;
    private float mPreValue = 0f;
    private float mAngleOffset = 22f;
    private float mAngleSweep = 180f - 2 * mAngleOffset;
    private float mScale = 1;
    private long mTime = 0;
    private int mRpm = 0;

    // string here
    private String mSpeedUnit = "km/h";
    private String mRpmUnit = "rpm";

    // size here
    private int mProgressWidth = 50; // hardcode?
    private int mSpeedTextSize = 200; // in pixel
    private int mTimeTextSize = 50; // in pixel
    private int mKMHTextSize = 50; // in pixel
    private int mRPMTextSize = 50;
    private int mRPMUnitTextSize = 30;
    private int margin = 0;
    private int mNewBitmapWidth = 0;
    private int mXOffset = 0;
    private int mYOffset = 0;
    private int mX0 = margin;
    private int mY0 = margin;
    private float mHardMargin = 25;

    private boolean mIsClickMap = false;
    private boolean mIsShow = true;
    private boolean mIsMoving = false;


    public SpeedMeterView(Context context) {
        super(context);
        init();
    }

    public SpeedMeterView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public void init() {
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inScaled = false;
        backBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.speedo_back, options);
        runBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.speedo_progress, options);
        pointerBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.speed_pointer, options);
        timeBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.speedo_time, options);
        rpmBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.speedo_rpm, options);
        startBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.speed_start, options);
        mBackPaint = new Paint(Paint.ANTI_ALIAS_FLAG);//消除锯齿
        mRunPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

        mSpeedTextSize = getResources().getDimensionPixelSize(R.dimen.speed_meter_speed);
        mSpeedTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mSpeedTextPaint.setTextSize(mSpeedTextSize);
        mSpeedTextPaint.setTextAlign(Paint.Align.CENTER);
        mSpeedTextPaint.setColor(Color.BLACK);
        mSpeedTextPaint.setTypeface(Typeface.DEFAULT_BOLD);

        mTimeTextSize = getResources().getDimensionPixelSize(R.dimen.speed_meter_time);
        mTimeTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mTimeTextPaint.setTextSize(mTimeTextSize);
        mTimeTextPaint.setTextAlign(Paint.Align.CENTER);
        mTimeTextPaint.setColor(mValueColor);
        mTimeTextPaint.setTypeface(Typeface.DEFAULT);

        mKMHTextSize = getResources().getDimensionPixelSize(R.dimen.speed_meter_speed_unit);
        mKMHTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mKMHTextPaint.setTextSize(mKMHTextSize);
        mKMHTextPaint.setTextAlign(Paint.Align.CENTER);
        mKMHTextPaint.setColor(mUnitColor);
        mKMHTextPaint.setTypeface(Typeface.DEFAULT);

        mRPMTextSize = getResources().getDimensionPixelSize(R.dimen.speed_meter_rpm);
        mRPMTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mRPMTextPaint.setTextSize(mRPMTextSize);
        mRPMTextPaint.setTextAlign(Paint.Align.CENTER);
        mRPMTextPaint.setColor(mValueColor);
        mRPMTextPaint.setTypeface(Typeface.DEFAULT);

        mRPMUnitTextSize = getResources().getDimensionPixelSize(R.dimen.speed_meter_rpm_unit);
        mRPMUnitTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mRPMUnitTextPaint.setTextSize(mRPMUnitTextSize);
        mRPMUnitTextPaint.setTextAlign(Paint.Align.CENTER);
        mRPMUnitTextPaint.setColor(mUnitColor);
        mRPMUnitTextPaint.setTypeface(Typeface.DEFAULT);


        mBackColorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mBackColorPaint.setColor(mBackGroundColor);
        mTestColorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mTestColorPaint.setColor(Color.BLUE);
    }

    private void scaleTextSize(float scale) {
        mSpeedTextSize *= scale; // in pixel
        mTimeTextSize *= scale; // in pixel
        mKMHTextSize *= scale; // in pixel
        mRPMTextSize *= scale;
        mRPMUnitTextSize *= scale;
    }

    public void performContinue() {
        mStatus = SPEED_STATUS.SPEED_STATUS_RUNNING;
        this.invalidate();
    }

    public void performStop() {
        mStatus = SPEED_STATUS.SPEED_STATUS_READY;
        // reset data();
        resetData();
        this.invalidate();
    }

    @Override
    public boolean performClick() {
        if (mIsMoving) {
            return true; // // TODO: 2016/3/1 need to double check;
        }

        if (mIsClickMap) {
            mStatus = SPEED_STATUS.SPEED_STATUS_MAP;
            mIsClickMap = false;
            return super.performClick();
        }

        if (mStatus == SPEED_STATUS.SPEED_STATUS_READY) {
            mStatus = SPEED_STATUS.SPEED_STATUS_RUNNING;
            this.invalidate();
        } else if (mStatus == SPEED_STATUS.SPEED_STATUS_RUNNING) {
//            mStatus = SPEED_STATUS.SPEED_STATUS_PAUSED; // // TODO: 2016/3/1  
        }
        return super.performClick();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        if (mStatus == SPEED_STATUS.SPEED_STATUS_RUNNING) {
            try {
                int x0 = margin + mXOffset;
                int y0 = margin + mYOffset;

                int x = (int) event.getX() - x0;
                int y = (int) event.getY() - y0;
                x /= mScale;
                y /= mScale;
                int color = backBitmap.getPixel(x, y);
                if (color == 0) {
                    mIsClickMap = true;
                }

            } catch (IllegalArgumentException exception) {

            }
        }

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                Log.d(TAG, "onTouchEvent: down");
                break;
            case MotionEvent.ACTION_UP:
                Log.d(TAG, "onTouchEvent: up");
                break;
            case MotionEvent.ACTION_MOVE:
                Log.d(TAG, "onTouchEvent: move");
                break;
            case MotionEvent.ACTION_CANCEL:
                break;
            default:
                break;
        }

        return super.onTouchEvent(event);
    }


    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        getParent().requestDisallowInterceptTouchEvent(true);
        return super.dispatchTouchEvent(ev);
    }


    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        Log.d(TAG, "onSizeChanged: " + w + "," + h + "," + oldw + "," + oldh);
//        Shader shader = new BitmapShader(runBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);//CLAMP 拉伸
//        Matrix localMatrix = new Matrix();
//        localMatrix.postTranslate(margin, margin);
//        shader.setLocalMatrix(localMatrix);
//        mBackPaint.setShader(shader);
//        mOval = new RectF(0, 0, runBitmap.getWidth(), runBitmap.getHeight());
//        mOval.offset(mX0, mY0);
    }

    // draw start page
    private void onDrawStart(Canvas canvas) {
        // start page background
        Log.d(TAG, "onDrawStart: ");
        RectF dst = new RectF(0, 0, mNewBitmapWidth, mNewBitmapWidth);
        dst.offset(mXOffset + margin, mYOffset + margin);
        canvas.drawBitmap(startBitmap, null, dst, mRunPaint);
        canvas.save();
    }

    private void onDrawRunning(Canvas canvas) {
        Log.d(TAG, "onDrawRunning: ");
        Shader shader = new BitmapShader(runBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);//CLAMP 拉伸
        Matrix localMatrix = new Matrix();
        localMatrix.postScale(mScale, mScale);
        localMatrix.postTranslate(margin + mXOffset, margin + mYOffset);
        shader.setLocalMatrix(localMatrix);
        mBackPaint.setShader(shader);
        mOval = new RectF(0, 0, mNewBitmapWidth, mNewBitmapWidth);
        mOval.offset(mXOffset + margin, mYOffset + margin);

        // background
        RectF dst = new RectF(0, 0, mNewBitmapWidth, mNewBitmapWidth);
        dst.offset(mXOffset + margin, mYOffset + margin);
        canvas.drawBitmap(backBitmap, null, dst, mRunPaint);

        // color back ground
        if (mXOffset > 0) {
            RectF leftDst = new RectF(0, 0, mXOffset, getHeight());
            canvas.drawRect(leftDst, mBackColorPaint);
            RectF rightDst = new RectF(getWidth() - mXOffset, 0, getWidth() + 1, getHeight());
            canvas.drawRect(rightDst, mBackColorPaint);
        }

        // progress
        float startAngle = mAngleOffset + mAngleSweep;
        canvas.drawArc(mOval, startAngle, mAngle, true, mBackPaint);

        // totle time & rpm
        float xCenter = getWidth() / 2;
        float yCenter = getHeight() / 2 + mYOffset / 2;
        float yImageBaseline = yCenter - 5f;
        float yNumberBaseline = yCenter + 50f;
        float halfImage = (float) (timeBitmap.getWidth()) * mScale / 2f;
        float progressWidth = (float) mProgressWidth * mScale;
        float r = (int) (mNewBitmapWidth / 2f - progressWidth - mHardMargin * mScale);
        float xImageCenterOffset = r / 2f * 1.3f; // modify the scale value
        float yImageCenterOffset = halfImage;
        float xImageLeftCenter = xCenter - xImageCenterOffset;
        float yImageLeftCenter = yImageBaseline - yImageCenterOffset;
        float xImageRightCenter = xCenter + xImageCenterOffset;
        float yImageRightCenter = yImageBaseline - yImageCenterOffset;
        RectF leftImageRect = new RectF(xImageLeftCenter - halfImage,
                yImageLeftCenter - halfImage,
                xImageLeftCenter + halfImage,
                yImageLeftCenter + halfImage);

        RectF rightImageRect = new RectF(xImageRightCenter - halfImage,
                yImageRightCenter - halfImage,
                xImageRightCenter + halfImage,
                yImageRightCenter + halfImage);

        canvas.drawBitmap(timeBitmap, null, leftImageRect, mRunPaint);
        canvas.drawBitmap(rpmBitmap, null, rightImageRect, mRunPaint);

        canvas.drawText(FormatUtils.formatShortTime(mTime), xImageLeftCenter, yNumberBaseline, mTimeTextPaint);
        canvas.drawText(mSpeedUnit, xCenter, yNumberBaseline, mKMHTextPaint);
        float rpmValueWidth = FormatUtils.getTextWidth(mRPMTextPaint, String.valueOf(mRpm));
        float rpmUnitWidth = FormatUtils.getTextWidth(mRPMUnitTextPaint, mRpmUnit);
        float rpmHalfWidth = (rpmValueWidth + rpmUnitWidth) / 2f;
        float rpmValueCenter = 0;
        float rpmUnitCenter = 0;
        if (rpmValueWidth > rpmUnitWidth) {
            float delta = rpmValueWidth - rpmHalfWidth;
            rpmValueCenter = xImageRightCenter - rpmValueWidth / 2f + delta;
            rpmUnitCenter = rpmValueCenter + rpmHalfWidth;
        } else {
            float delta = rpmUnitWidth - rpmHalfWidth;
            rpmUnitCenter = xImageRightCenter + delta;
            rpmValueCenter = rpmUnitCenter - rpmHalfWidth;
        }
        canvas.drawText(String.valueOf(mRpm), rpmValueCenter, yNumberBaseline, mRPMTextPaint);
        canvas.drawText(mRpmUnit, rpmUnitCenter, yNumberBaseline, mRPMUnitTextPaint);


//        // test lines
//        canvas.drawLine(0, yImageBaseline - mSpeedTextSize, getWidth(), yImageBaseline - mSpeedTextSize, mTestColorPaint);
//        canvas.drawLine(0, yImageBaseline, getWidth(), yImageBaseline, mTestColorPaint);
//        canvas.drawLine(0, yNumberBaseline, getWidth(), yNumberBaseline, mTestColorPaint);
//        canvas.drawLine(xCenter, 0, xCenter, getHeight(), mTestColorPaint);
//        canvas.drawLine(xImageLeftCenter - halfImage, 0, xImageLeftCenter - halfImage, getHeight(), mTestColorPaint);
//        canvas.drawLine(xImageLeftCenter + halfImage, 0, xImageLeftCenter + halfImage, getHeight(), mTestColorPaint);
//        canvas.drawLine(0, yImageLeftCenter - halfImage, getWidth(), yImageLeftCenter - halfImage, mTestColorPaint);
//        canvas.drawLine(0, yImageLeftCenter + halfImage, getWidth(), yImageLeftCenter + halfImage, mTestColorPaint);
//        // end of test lines
//        canvas.drawRect(leftImageRect, mTestColorPaint);
//        canvas.drawBitmap(timeBitmap, null, leftImageRect, mRunPaint);


        canvas.save();
        float textWidthScale = 0.8f;
        canvas.scale(textWidthScale, 1f);
        // speed number
        canvas.drawText(String.valueOf((int) mCurValue), xCenter / textWidthScale, yImageBaseline, mSpeedTextPaint);
        canvas.restore();

        // speed pointer
        canvas.save();
        int pointerWidth = (int) ((float) pointerBitmap.getWidth() * mScale);
        int pointerHeight = (int) ((float) pointerBitmap.getHeight() * mScale);
        int x0 = getWidth() / 2;
        int y0 = getHeight() / 2 + mYOffset / 2;

        int halfStrokeWidth = (int) ((float) mProgressWidth * mScale / 2f);
        int radius = (int) (mNewBitmapWidth / 2 - halfStrokeWidth - mHardMargin * mScale);

        canvas.rotate(mAngle - mAngleOffset, x0, y0);

        x0 -= radius;
        RectF pointerDst = new RectF(x0 - pointerWidth / 2,
                y0 - pointerHeight / 2,
                x0 + pointerWidth / 2,
                y0 + pointerHeight / 2);

        canvas.drawBitmap(pointerBitmap, null, pointerDst, mRunPaint);
        canvas.restore();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        if (!mIsShow) {
            return;
        }

        int width = getWidth();
        int height = getHeight();
        mNewBitmapWidth = (width < height) ? width : height;
        mNewBitmapWidth -= margin * 2;

        mXOffset = (width - mNewBitmapWidth) / 2;
        mYOffset = height - mNewBitmapWidth; // align parent bottom
        mScale = (float) mNewBitmapWidth / (float) backBitmap.getWidth();

        mX0 = mXOffset;
        mY0 = mYOffset;


        LogUtils.i(TAG, "onDraw " + "w="+ width + ", h=" + height );

        if (mStatus == SPEED_STATUS.SPEED_STATUS_READY) {
            onDrawStart(canvas);
        } else if (mStatus == SPEED_STATUS.SPEED_STATUS_RUNNING ||
                mStatus == SPEED_STATUS.SPEED_STATUS_PAUSED) {
            onDrawRunning(canvas);
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//        int measuredHeight = runBitmap.getHeight() + 2 * margin;
//        int measuredWidth = runBitmap.getWidth() + 2 * margin;
//        int measuredHeight = measureHeight(heightMeasureSpec);
//        int measuredWidth = measureWidth(widthMeasureSpec);
//        Log.d(TAG, "onMeasure: " + widthMeasureSpec + ":" + heightMeasureSpec);
//        int viewWidthHeight = (widthMeasureSpec>heightMeasureSpec ? heightMeasureSpec : widthMeasureSpec);
//        setMeasuredDimension(viewWidthHeight, viewWidthHeight);
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        int viewWidthHeight = widthSize > heightSize ? heightSize : widthSize;
        setMeasuredDimension(viewWidthHeight,viewWidthHeight);
        LogUtils.i(TAG, "onMeasure " + "w="+ viewWidthHeight + ", h=" + viewWidthHeight );
    }

    /**
     * Determines the width of this view
     *
     * @param measureSpec A measureSpec packed into an int
     * @return The width of the view, honoring constraints from measureSpec
     */
    private int measureWidth(int measureSpec) {
        int result = 0;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);

        if (specMode == MeasureSpec.EXACTLY) {
            // We were told how big to be
            result = specSize;
        } else {
            // Measure the text
            result = runBitmap.getWidth() + getPaddingLeft()
                    + getPaddingRight();
            if (specMode == MeasureSpec.AT_MOST) {
                // Respect AT_MOST value if that was what is called for by measureSpec
                result = Math.min(result, specSize);
            }
        }

        return result;
    }

    /**
     * Determines the height of this view
     *
     * @param measureSpec A measureSpec packed into an int
     * @return The height of the view, honoring constraints from measureSpec
     */
    private int measureHeight(int measureSpec) {
        int result = 0;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);

        if (specMode == MeasureSpec.EXACTLY) {
            // We were told how big to be
            result = specSize;
        } else {
            // Measure the text (beware: ascent is a negative number)
            result = runBitmap.getHeight() + getPaddingTop()
                    + getPaddingBottom();
            if (specMode == MeasureSpec.AT_MOST) {
                // Respect AT_MOST value if that was what is called for by measureSpec
                result = Math.min(result, specSize);
            }
        }
        return result;
    }

    // m/s
    public void setValue(float value) {
        LogUtils.d(TAG, "setValue: @" + value);
        if (Float.compare(value, mMaxValue) > 0) {
            value = mMaxValue;
        }

        if (Float.compare(this.mCurValue, value) == 0) {
            return;
        }
        this.mPreValue = this.mCurValue;
        this.mCurValue = value;
        this.mPreAngle = this.mAngle;
        this.mAngle = getAngle();
        invalidate();

//        // add animator here
//        ValueAnimator animator = ValueAnimator.ofFloat(mPreValue, mCurValue);
//        if ((mPreValue > mCurValue) ) {
//            animator.setInterpolator(new DecelerateInterpolator());
//        }else {
//            animator.setInterpolator(new AccelerateInterpolator());
//        }
//        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
//            public void onAnimationUpdate(ValueAnimator animation) {
//                float value = (Float) animation.getAnimatedValue();
//                mAngle = getAngle(value);
//                invalidate();
//            }
//        });
//        animator.setDuration(900);
//        animator.start();
    }

    public void setTime(long time) {
        this.mTime = time;
        invalidate();
    }

    public void setRpm(int rpm) {
        this.mRpm = rpm;
    }

    private float getAngle() {
        return (mCurValue / mMaxValue) * (360f - mAngleSweep);
    }

    private float getAngle(float value) {
        return (value / mMaxValue) * (360f - mAngleSweep);
    }

    public SPEED_STATUS getSpeedStatus() {
        return mStatus;
    }

    private void resetData() {
        mAngle = 0f;
        mPreAngle = 0f;
        mCurValue = 0f;
        mPreValue = 0f;
    }

    public void showSpeedMeter(boolean isShow) {
        mIsShow = isShow;
        if (mIsShow && mStatus == SPEED_STATUS.SPEED_STATUS_MAP) {
            mStatus = SPEED_STATUS.SPEED_STATUS_RUNNING;
        }
        invalidate();
    }

    public void setMove(boolean isMove) {
        mIsMoving = isMove;
    }
}